package com.app.aspect;

import cn.hutool.core.date.SystemClock;
import cn.hutool.core.util.URLUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.app.model.SysLog;
import com.app.service.ISysLogService;
import com.app.util.IpUtil;
import com.app.util.TokenUtils;
import com.baomidou.mybatisplus.core.toolkit.ArrayUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @ClassName SysLogAspect
 * @Description TODO
 * @Author yoominic
 * @Date 2022/10/17 15:08 周一
 * @Version 1.0.0
 **/
@Aspect // 切面 定义了通知和切点的关系
@Component
@Slf4j
public class SysLogAspect {
    @Autowired
    private ISysLogService sysLogService;

    private static HttpServletRequest getRequest() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        return request;
    }

    @Autowired
    private ApplicationContext applicationContext;

    @Pointcut("@annotation(com.app.aspect.Log)")
    public void operationLogPointCut() {
    }

    @AfterReturning(value = "operationLogPointCut()", returning = "keys")
    @SuppressWarnings("unchecked")
    public void saveOperationLog(JoinPoint joinPoint, Object keys) {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = (HttpServletRequest) Objects.requireNonNull(requestAttributes).resolveReference(RequestAttributes.REFERENCE_REQUEST);
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        // JointPoint 对象很有用，可以用它来获取一个签名，
        // 利用签名可以获取请求的包名、方法名，包括参数（通过 joinPoint.getArgs() 获取）等。
        // 获取方法上的HasPermissions注解信息
        Log annotation = method.getAnnotation(Log.class);
        String authority = annotation.value();
        log.info("日志访问标识名称:{}", authority);
        long beginTime = SystemClock.now();

        //执行时长(毫秒)
        long time = SystemClock.now() - beginTime;
        SysLog sysLogEntity = new SysLog();
        if (log != null) {
            //注解上的描述
            sysLogEntity.setTitle(authority);
        }

        if (joinPoint.getArgs().length > 0) {
            //获取传参信息
            Object[] args = joinPoint.getArgs();
            //过滤无法序列化
            Stream<?> stream = ArrayUtils.isEmpty(args) ? Stream.empty() : Arrays.stream(args);
            List<Object> logArgs = stream
                    .filter(arg -> (!(arg instanceof HttpServletRequest) && !(arg instanceof HttpServletResponse)))
                    .collect(Collectors.toList());
            //过滤后序列化无异常
            String requestParam = JSON.toJSONString(logArgs);
            if (joinPoint.getArgs()[0] instanceof MultipartFile) {
                sysLogEntity.setParams("file");
            } else {
                log.info(JSONUtil.toJsonStr(joinPoint));
                sysLogEntity.setParams(JSON.toJSONString(requestParam));
            }
        }
        //请求的方法名
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        sysLogEntity.setMethod(className + "." + methodName + "()");
        //设置IP地址
        sysLogEntity.setRemoteAddr(IpUtil.getIpAddress(request));
        sysLogEntity.setRequestUri(URLUtil.getPath(request.getRequestURI()));
        sysLogEntity.setMethod(request.getMethod());
        sysLogEntity.setUserAgent(IpUtil.getUserAgent(request).toString());
        sysLogEntity.setIpSources(IpUtil.getIpSource(IpUtil.getIpAddress(request)).toString());
        sysLogEntity.setResponseParams(JSON.toJSONString(keys));
        //用户名
        String token = request.getHeader("authori-zation");
        sysLogEntity.setRequestUri(getRequest().getRequestURI());
        if (token != null) {
            sysLogEntity.setUserId(TokenUtils.verify(token));
        }
        sysLogEntity.setRequestUri(getRequest().getRequestURI());
        sysLogEntity.setCreateTime(LocalDateTime.now());
        sysLogEntity.setType(String.valueOf(2));
        //保存系统日志
        sysLogService.save(sysLogEntity);

    }

}
